﻿using System.Collections.Generic;
using iStudio.Library.Dsp;
using iStudio.Structure.Engine;

namespace iStudio.Module.Equalizer
{
    internal class EqualizerEffect : ICustomSampleProvider
    {
        private readonly IList<SignalFilter> _filters;
        private readonly EqualizerCache _cache;

        public float[] Values
        {
            get;
            set;
        }

        public EqualizerEffect()
        {
            Values = new float[8];

            

            _filters = new List<SignalFilter>();

            SignalFilter filter = new SignalFilter( new[] { 1, -4.53905152053491, 8.26066093268146, -7.53334037712995, 3.44208741939080, -0.630309447578747 }, 
                                                    new[] { 1.46896339522917e-06, 7.34481697614586e-06, 1.46896339522917e-05, 1.46896339522917e-05, 7.34481697614586e-06, 1.46896339522917e-06 });

            _filters.Add(filter);

            filter = new SignalFilter(new[] { 1, -9.34574212216419, 39.4948943534915, -99.3812445366928, 164.893432256478, -188.496820692503, 150.348588200915, -82.6227860794968, 29.9393835512188, -6.46001429199645, 0.630309447578750 },
                                        new[] { 1.46896347456126e-06, 0, -7.34481737280629e-06, 0, 1.46896347456126e-05, 0, -1.46896347456126e-05, 0, 7.34481737280629e-06, 0, -1.46896347456126e-06 });

            _filters.Add(filter);

            filter = new SignalFilter(new[] { 1, -8.96304075090452, 36.6906769257444, -90.2794982877909, 147.810725300573, -168.224350909685, 134.773188499729, -75.0562336331500, 27.8137251897217, -6.19548138539704, 0.630309447578743 },
                                        new[] { 1.46896321550642e-06, 0, -7.34481607753209e-06, 0, 1.46896321550642e-05, 0, -1.46896321550642e-05, 0, 7.34481607753209e-06, 0, -1.46896321550642e-06 });

            _filters.Add(filter);

            filter = new SignalFilter(new[] { 1, -8.39870287087878, 32.7694251126665, -78.0016294349095, 125.244626290260, -141.628260639776, 114.198155717889, -64.8492368753512, 24.8413302457602, -5.80539671123989, 0.630309447578744 },
                                        new[] { 1.46896334858709e-06, 0, -7.34481674293545e-06, 0, 1.46896334858709e-05, 0, -1.46896334858709e-05, 0, 7.34481674293545e-06, 0, -1.46896334858709e-06 });

            _filters.Add(filter);

            filter = new SignalFilter(new[] { 1, -7.66416481994833, 28.0473860901028, -63.9340043077536, 100.115071787569, -112.282845092894, 91.2857285756651, -53.1542852886504, 21.2619210201029, -5.29766535668303, 0.630309447578750 },
                                        new[] { 1.46896340336359e-06, 0, -7.34481701681797e-06, 0, 1.46896340336359e-05, 0, -1.46896340336359e-05, 0, 7.34481701681797e-06, 0, -1.46896340336359e-06 });

            _filters.Add(filter);

            filter = new SignalFilter(new[] { 1, -6.77431205170358, 22.9053901582489, -49.5368728857768, 75.2919096890670, -83.6132736060698, 68.6525011839507, -41.1852903102466, 17.3641752393451, -4.68257652526738, 0.630309447578744 },
                                        new[] { 1.46896339395126e-06, 0, -7.34481696975630e-06, 0, 1.46896339395126e-05, 0, -1.46896339395126e-05, 0, 7.34481696975630e-06, 0, -1.46896339395126e-06 });

            _filters.Add(filter);

            //filter = new SignalFilter(new[] { 1, -2.87884337732739, 6.05543076163367, -8.43098864899503, 9.64727504850003, -8.34431649462856, 6.03973815373210, -3.28921303659714, 1.46908406933270, -0.425745281730447, 0.0924867343727318 },
            //                            new[] { 0.00222801662205549, 0, -0.0111400831102775, 0, 0.0222801662205549, 0, -0.0222801662205549, 0, 0.0111400831102775, 0, -0.00222801662205549 });

            //_filters.Add(filter);

            //filter = new SignalFilter(new[] { 1, -2.15105711021124e-16, 0.633436854000505, -7.25232935581156e-17, 0.0557280900008410, -3.09353043186585e-18 },
            //                            new[] { 0.0527864045000421, -0.263932022500210, 0.527864045000421, -0.527864045000421, 0.263932022500210, -0.0527864045000421 });

            //_filters.Add(filter);

            _cache = new EqualizerCache (_filters.Count);
        }

        public void Process(float[] buffer, int offset, int count)
        {
            List<float> tempBuffer = new List<float> ();

            for (int i = count - 10; i < count; i++)
            {
                tempBuffer.Add (buffer[i]);
            }

            //
            // Create filtered outputs
            //

            List<double[]> filtered = new List<double[]>();

            //Parallel.For(0, _filters.Count, i =>
            //{
            //    filtered.Add(Filter(buffer, _filters[i], count));
            //});

            for (int i = 0; i < _filters.Count; i++)
            {
                filtered.Add(Filter(buffer, _filters[i], count, i));

                _cache.SetCache(i, filtered[filtered.Count - 1], count - 10, 10);
            }

            _cache.SetRawCache(tempBuffer);

            for (int i = 0; i < count; i++)
            {
                double value = 0.0f;

                for (int x = 0; x < filtered.Count; x++)
                {
                    value += (filtered[x][i] * (Values[x] + 1) / 2);
                }

                //if (_hammingWindow != null)
                //{
                //    buffer[i] = (float)(value * _hammingWindow[i]);
                //}

                buffer[i] = (float) (value);

                if (buffer[i] > 1.0f)
                {
                    buffer[i] = 1.0f;
                }

                if (buffer[i] < -1.0f)
                {
                    buffer[i] = -1.0f;
                }
            }
        }

        private double[] Filter(float[] buffer, SignalFilter filter, int count, int index)
        {
            double[] y = new double[count];

            for (int n = 0; n < count; n++)
            {
                y[n] = 0.0;

                for (int k = 0; k < filter.B.Count; k++)
                {
                    if (n - k >= 0)
                    {
                        y[n] = y[n] + filter.B[k] * buffer[n - k];
                    }
                    else if (_cache.GetRawCache ().Count > 0)
                    {
                        double cached =  _cache.GetRawCache ()[_cache.GetRawCache().Count + (n - k)];

                        y[n] = y[n] + filter.B[k] * cached;
                    }
                }

                for (int k = 1; k < filter.A.Count; k++)
                {
                    if (n - k >= 0)
                    {
                        y[n] = y[n] - filter.A[k] * y[n - k];
                    }
                    else if (_cache.GetCache(index).Count > 0)
                    {
                        double cached =  _cache.GetCache(index)[_cache.GetCache(index).Count + (n - k)];

                        y[n] = y[n] - filter.A[k] * cached;
                    }
                }
            }

            return y;
        }
    }
}
